﻿#include "stdafx.h"
#include "qg/qgimage.h"

#define CCMW	qgCcmWork

//////////////////////////////////////////////////////////////////////////
// 색깔 변경

//
void qgCcmWork::COPY_1_16(kint width, kint height, const kbyte* in, int linepad, kushort* out, kint pitch, bool flip)
{
	kint x, y, shift;

	if (!in || !out)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		shift = 7;

		for (x = 0; x < width; ++x)
		{
			out[x] = *in >> shift & 0x01 ? (kushort)0xffff : (kushort)0x8000;

			if ((--shift) < 0) // 8 pixel done
			{
				shift = 7;
				++in;
			}
		}

		if (shift != 7) // width did not fill last byte
			++in;

		out += pitch;
		in += linepad;
	}
}

//
void qgCcmWork::COPY_1_32(kint width, kint height, const kbyte* in, int linepad, kint* out, kint pitch, bool flip)
{
	kint x, y, shift;

	if (!in || !out)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		shift = 7;

		for (x = 0; x < width; ++x)
		{
			out[x] = *in >> shift & 0x01 ? 0xFFFFFFFF : 0x80000000;

			if ((--shift) < 0) // 8 pixel done
			{
				shift = 7;
				++in;
			}
		}

		if (shift != 7) // width did not fill last byte
			++in;

		out += pitch;
		in += linepad;
	}
}

//
void qgCcmWork::COPY_4_16(kint width, kint height, const kbyte* in, int linepad, kushort* out, kint pitch, const kint* palette, bool flip)
{
	kint x, y, shift;

	if (!in || !out || !palette)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		shift = 4;

		for (x = 0; x < width; ++x)
		{
			out[x] = TRN_RGBA32_RGBA16(palette[(kbyte)((*in >> shift) & 0xf)]);

			if (shift == 0)
			{
				shift = 4;
				++in;
			}
			else
				shift = 0;
		}

		if (shift == 0) // odd width
			++in;

		out += pitch;
		in += linepad;
	}
}

//
void qgCcmWork::COPY_BGR_4_32(kint width, kint height, const kbyte* in, int linepad, kint* out, kint pitch, const kint* palette, bool flip)
{
	kint x, y, shift;

	if (!in || !out || !palette)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		shift = 4;

		for (x = 0; x < width; ++x)
		{
			out[x] = TRN_RGBA32_BGRA32(palette[(kbyte)((*in >> shift) & 0xf)]);

			if (shift == 0)
			{
				shift = 4;
				++in;
			}
			else
				shift = 0;
		}

		if (shift == 0) // odd width
			++in;

		out += pitch;
		in += linepad;
	}
}

//
void qgCcmWork::COPY_8_16(kint width, kint height, const kbyte* in, int linepad, kshort* out, kint pitch, const kint* palette, bool flip)
{
	kint x, y;

	if (!in || !out || !palette)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		for (x = 0; x < width; ++x)
		{
			out[x] = TRN_RGB32_RGBA16(palette[(kbyte)(*in)]);
			++in;
		}

		out += pitch;
		in += linepad;
	}
}

//
void qgCcmWork::COPY_BGR_8_32(kint width, kint height, const kbyte* in, int linepad, kint* out, kint pitch, const kint* palette, bool flip)
{
	kint x, y;
	register kuint c;

	if (!in || !out)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		if (palette)
		{
			for (x = 0; x < width; x += 1)
			{
				c = in[x];
				out[x] = CCMW::TRN_RGBA32_BGRA32(palette[c]);
			}
		}
		else
		{
			for (x = 0; x < width; x += 1)
			{
				c = in[x];
				out[x] = K_MAKECOLOR(0xFF, c, c, c);
			}
		}

		out += pitch;
		in += width + linepad;
	}
}

//
void qgCcmWork::COPY_16_16(kint width, kint height, const kshort* in, int linepad, kshort* out, kint pitch, bool flip)
{
	kint y;

	if (!in || !out)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
#if _SB_BIGENDIAN_
		for (kint x = 0; x < width; ++x)
			out[x] = K_ENDIAN_SWAP_USHORT(in[x]);
#else
		memcpy(out, in, width * sizeof(kshort));
#endif

		out += pitch;
		in += width + linepad;
	}
}

//
void qgCcmWork::COPY_16_32(kint width, kint height, const kshort* in, int linepad, kint* out, kint pitch, bool flip)
{
	kint y;

	if (!in || !out)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		for (kint x = 0; x < width; ++x)
			out[x] = (kint)TRN_RGB16_RGBA32((kushort)in[x]);

		out += pitch;
		in += width + linepad;
	}
}

//
void qgCcmWork::COPY_BGR_16_32(kint width, kint height, const kshort* in, int linepad, kint* out, kint pitch, bool flip)
{
	kint y;

	if (!in || !out)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		for (kint x = 0; x < width; ++x)
			out[x] = (kint)TRN_RGB16_BGRA32((kushort)in[x]);

		out += pitch;
		in += width + linepad;
	}
}

//
void qgCcmWork::COPY_24_32(kint width, kint height, const kbyte* in, int linepad, kint* out, kint pitch, bool flip)
{
	kint x, y, t, lineWidth;

	if (!in || !out)
		return;

	lineWidth = 3 * width;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		for (t = x = 0; x < lineWidth; t++, x += 3)
			out[t] = K_MAKECOLOR(0xFF, in[x + 2], in[x + 1], in[x]);

		out += pitch;
		in += lineWidth + linepad;
	}
}

//
void qgCcmWork::COPY_BGR_24_32(kint width, kint height, const kbyte* in, int linepad, kint* out, kint pitch, bool flip)
{
	kint x, y, t, lineWidth;

	if (!in || !out)
		return;

	lineWidth = 3 * width;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		for (t = x = 0; x < lineWidth; t++, x += 3)
			out[t] = K_MAKECOLOR(0xFF, in[x], in[x + 1], in[x + 2]);

		out += pitch;
		in += lineWidth + linepad;
	}
}

//
void qgCcmWork::COPY_32_32(kint width, kint height, const kint* in, int linepad, kint* out, kint pitch, bool flip)
{
	kint y;

	if (!in || !out)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
#if _SB_BIGENDIAN_
		for (kint x = 0; x < width; ++x)
			out[x] = K_ENDIAN_SWAP_UINT(in[x]);
#else
		memcpy(out, in, width * sizeof(kint));
#endif

		out += pitch;
		in += width + linepad;
	}
}

//
void qgCcmWork::COPY_BGR_32_32(kint width, kint height, const kint* in, int linepad, kint* out, kint pitch, bool flip)
{
	kint y;

	if (!in || !out)
		return;

	if (flip)
	{
		out += pitch * height - pitch;
		pitch = -pitch;
	}

	for (y = 0; y < height; ++y)
	{
		for (kint x = 0; x < width; ++x)
			out[x] = CCMW::TRN_RGBA32_BGRA32(in[x]);

		out += pitch;
		in += width + linepad;
	}
}


//////////////////////////////////////////////////////////////////////////
// 블릿 워크

//
void qgBlitWork::ColorConvert(kconstpointer sp, qgColorFormat sf, kint sn, kpointer dp, qgColorFormat df)
{
	kint x;
	kanyptr s, d;

	switch ((ksize_t)sf)
	{
		case QGCF16_RGB:
			switch ((ksize_t)df)
			{
				case QGCF16_RGBA:
					s.u16 = (kushort*)sp;
					d.u16 = (kushort*)dp;
					for (x = 0; x < sn; x++)
						*d.u16++ = CCMW::TRN_RGB16_RGBA16(*s.u16++);
					break;

				case QGCF16_RGB:
					memcpy(dp, sp, sn * 2);
					break;

				case QGCF32_RGBA:
					s.u16 = (kushort*)sp;
					d.u32 = (kuint*)dp;
					for (x = 0; x < sn; x++)
						*d.u32++ = CCMW::TRN_RGB16_RGBA32(*s.u16++);
					break;

				case QGCF32_BGRA:
					s.u16 = (kushort*)sp;
					d.u32 = (kuint*)dp;
					for (x = 0; x < sn; x++)
						*d.u32++ = CCMW::TRN_RGB16_BGRA32(*s.u16++);
					break;
			}
			break;

		case QGCF16_RGBA:
			switch ((ksize_t)df)
			{
				case QGCF16_RGBA:
					memcpy(dp, sp, sn * 2);
					break;

				case QGCF16_RGB:
					s.u16 = (kushort*)sp;
					d.u16 = (kushort*)dp;
					for (x = 0; x < sn; x++)
						*d.u16++ = CCMW::TRN_RGBA16_RGB16(*s.u16++);
					break;

				case QGCF32_RGBA:
					s.u16 = (kushort*)sp;
					d.u32 = (kuint*)dp;
					for (x = 0; x < sn; x++)
						*d.u32++ = CCMW::TRN_RGBA16_RGBA32(*s.u16++);
					break;

				case QGCF32_BGRA:
					s.u16 = (kushort*)sp;
					d.u32 = (kuint*)dp;
					for (x = 0; x < sn; x++)
						*d.u32++ = CCMW::TRN_RGBA16_BGRA32(*s.u16++);
					break;
			}
			break;

		case QGCF32_RGBA:
			switch ((ksize_t)df)
			{
				case QGCF16_RGBA:
					s.u32 = (kuint*)sp;
					d.u16 = (kushort*)dp;
					for (x = 0; x < sn; x++)
						*d.u16++ = CCMW::TRN_RGBA32_RGBA16(*s.u32++);
					break;

				case QGCF16_RGB:
#if 1
					s.u32 = (kuint*)sp;
					d.u16 = (kushort*)dp;
					for (x = 0; x < sn; x++)
						*d.u16++ = CCMW::TRN_RGBA32_RGB16(*s.u32++);
					break;
#else
					s.u8 = (kbyte*)sp;
					d.u16 = (kushort*)dp;
					for (x = 0; x < sn; x++)
					{
						kuint r = s.u8[2] >> 3, g = s.u8[1] >> 2, b = s.u8[0] >> 3;
						*d.u16 = (kushort)((r << 11) | (g << 5) | b);
						s.u8 += 4;
						d.u16++;
					}
#endif
					break;

				case QGCF32_RGBA:
					memcpy(dp, sp, sn * 4);
					break;

				case QGCF32_BGRA:
					s.u32 = (kuint*)sp;
					d.u32 = (kuint*)dp;
					for (x = 0; x < sn; x++)
						*d.u32 = CCMW::TRN_RGBA32_BGRA32(*s.u32++);
					break;
			}
			break;

		case QGCF32_BGRA:
			switch ((ksize_t)df)
			{
				case QGCF16_RGBA:
					s.u32 = (kuint*)sp;
					d.u16 = (kushort*)dp;
					for (x = 0; x < sn; x++)
						*d.u16++ = CCMW::TRN_BGRA32_RGBA16(*s.u32++);
					break;

				case QGCF16_RGB:
					s.u32 = (kuint*)sp;
					d.u16 = (kushort*)dp;
					for (x = 0; x < sn; x++)
						*d.u16++ = CCMW::TRN_BGRA32_RGB16(*s.u32++);
					break;

				case QGCF32_RGBA:
					s.u32 = (kuint*)sp;
					d.u32 = (kuint*)dp;
					for (x = 0; x < sn; x++)
						*d.u32 = CCMW::TRN_BGRA32_RGBA32(*s.u32++);
					break;

				case QGCF32_BGRA:
					memcpy(dp, sp, sn * 4);
					break;
			}
			break;
	}
}

//
qgClipBound qgBlitWork::GetClip(const tpl::Rect& r, const tpl::Point& p)
{
	qgClipBound ret = QG_CLIPBOUND_NONE;

	if (p.x < r.left)
		ret = QG_CLIPBOUND_LEFT;
	else if (p.x > r.right)
		ret = QG_CLIPBOUND_RIGHT;

	if (p.y < r.top)
		ret = (qgClipBound)(ret | QG_CLIPBOUND_TOP);
	else if (p.y > r.bottom)
		ret = (qgClipBound)(ret | QG_CLIPBOUND_BOTTOM);

	return ret;
}

//
tpl::Rect qgBlitWork::BuildClip(const tpl::Rect* clip, qgImage* img, bool passnative)
{
	if (clip && !img && passnative)
		return *clip;

	kint w = img ? img->GetWidth() : 0;
	kint h = img ? img->GetHeight() : 0;

	if (!clip)
		return tpl::Rect(0, 0, w, h);
	else
	{
		tpl::Rect rt;
		rt.left = K_CLAMP(clip->left, 0, w);
		rt.top = K_CLAMP(clip->top, 0, h);
		rt.right = K_CLAMP(clip->right, rt.left, w);
		rt.bottom = K_CLAMP(clip->bottom, rt.top, h);
		return rt;
	}
}

//
tpl::Rect qgBlitWork::MakeClip(qgImage* img)
{
	return tpl::Rect(0, 0, img->GetWidth() - 1, img->GetHeight() - 1);
}

//
kint qgBlitWork::ClipLine(const tpl::Rect& clip, tpl::Point* p0, tpl::Point* p1, const tpl::Point& in0, const tpl::Point& in1)
{
	kint code, code0, code1;

	//
	*p0 = in0;
	*p1 = in1;

	code0 = GetClip(clip, in0);
	code1 = GetClip(clip, in1);

	//
	while (code0 | code1)
	{
		kint x = 0, y = 0;

		if (code0 & code1)
			return 0;

		code = code0 ? 0 : code1;

		if ((code & QG_CLIPBOUND_BOTTOM) != 0)
		{
			y = clip.bottom;
			x = p0->x + (p1->x - p0->x) * (y - p0->y) / (p1->y - p0->y);
		}
		else if ((code & QG_CLIPBOUND_TOP) != 0)
		{
			y = clip.top;
			x = p0->x + (p1->x - p0->x) * (y - p0->y) / (p1->y - p0->y);
		}
		else if ((code & QG_CLIPBOUND_RIGHT) != 0)
		{
			x = clip.right;
			y = p0->y + (p1->y - p0->y) * (x - p0->x) / (p1->x - p0->x);
		}
		else if ((code & QG_CLIPBOUND_LEFT) != 0)
		{
			x = clip.left;
			y = p0->y + (p1->y - p0->y) * (x - p0->x) / (p1->x - p0->x);
		}

		if (code == code0)
		{
			p0->x = x;
			p0->y = y;
			code0 = GetClip(clip, *p0);
		}
		else
		{
			p1->x = x;
			p1->y = y;
			code1 = GetClip(clip, *p1);
		}
	}

	return 1;
}

//
kuint qgBlitWork::ExtractAlpha(kuint c)
{
	return (c >> 24) | (c >> 31);
}

//
kuint qgBlitWork::PackAlpha(kuint c)
{
	return (c > 127 ? c - 1 : c) << 24;
}

//
kuint qgBlitWork::PixelLerp(kuint s, kuint v)
{
	kuint rb = s & 0x00FF00FF;
	kuint xg = (s & 0xFF00FF00) >> 8;

	rb *= v;
	xg *= v;

	rb >>= 8;

	rb &= 0x00FF00FF;
	xg &= 0xFF00FF00;

	return rb | xg;
}

//
kuint qgBlitWork::PixelBlend(kuint c2, kuint c1, kuint alpha)
{
	kuint srb = c1 & 0x00FF00FF;
	kuint sxg = c1 & 0x0000FF00;
	kuint drb = c2 & 0x00FF00FF;
	kuint dxg = c2 & 0x0000FF00;
	kuint rb = srb - drb;
	kuint xg = sxg - dxg;

	rb *= alpha;
	xg *= alpha;

	rb >>= 8;
	xg >>= 8;

	rb += drb;
	xg += dxg;

	rb &= 0x00FF00FF;
	xg &= 0x0000FF00;

	return rb | xg;
}

//
kushort qgBlitWork::PixelBlend16(kushort c2, kuint c1, kushort alpha)
{
	kushort srb = c1 & 0x7c1f;
	kushort sxg = c1 & 0x03e0;
	kushort drb = c2 & 0x7c1f;
	kushort dxg = c2 & 0x03e0;
	kuint rb = srb - drb;
	kuint xg = sxg - dxg;

	rb *= alpha;
	xg *= alpha;

	rb >>= 5;
	xg >>= 5;

	rb += drb;
	xg += dxg;

	rb &= 0x7c1f;
	xg &= 0x03e0;

	return (kushort)(rb | xg);
}

// res = dst * (1 - srcalp) + src * srcalp
kuint qgBlitWork::PixelInvBlend(kuint c2, kuint c1)
{
	kuint alpha = c1 & 0xFF000000;
	kuint srb, sxg, drb, dxg, rb, xg;

	if (alpha == 0)
		return c2;

	if (alpha == 0xFF000000)
		return c1;

	alpha >>= 24;
	alpha += (alpha >> 7);

	//
	srb = c1 & 0x00FF00FF;
	sxg = c1 & 0x0000FF00;
	drb = c2 & 0x00FF00FF;
	dxg = c2 & 0x0000FF00;
	rb = srb - drb;
	xg = sxg - dxg;

	rb *= alpha;
	xg *= alpha;

	rb >>= 8;
	xg >>= 8;

	rb += drb;
	xg += dxg;

	rb &= 0x00FF00FF;
	xg &= 0x0000FF00;

	return (c1 & 0xFF000000) | rb | xg;
}


//////////////////////////////////////////////////////////////////////////
// 블릿 데이터

//
struct BlitRes
{
	kpointer			data;
	kint				pitch;
	kint				pmul;
	tpl::Rect			rect;

	BlitRes()
		: data(NULL), pitch(0), pmul(0), rect(0, 0, 0, 0)
	{
	}
};

//
struct BlitData
{
public:
	typedef void(BlitData::*BlitFunc)();

private:
	kuint				_argb;

	BlitRes				_dst;
	BlitRes				_src;

	tpl::Point			_size;

	bool				_stretch;
	tpl::Vec2			_stsize;

public:
	BlitData(kuint argb)
		: _argb(argb)
		, _dst(), _src()
		, _size(0, 0)
		, _stretch(false), _stsize(1.0f, 1.0f)
	{
	}

	void BuildSourceRect(const tpl::Rect* clip, qgImage* img, bool passnative = false)
	{
		_src.rect = qgBlitWork::BuildClip(clip, img, passnative);
	}

	void BuildSourceRect(const tpl::Point& srcloc, const tpl::Point& dstloc)
	{
		_src.rect.left = srcloc.x + (_dst.rect.left - dstloc.x);
		_src.rect.top = srcloc.y + (_dst.rect.top - dstloc.y);
		_src.rect.right = _src.rect.left + _size.x;
		_src.rect.bottom = _src.rect.top + _size.y;
	}

	void BuildDestRect(const tpl::Rect* clip, qgImage* img, bool passnative = true)
	{
		_dst.rect = qgBlitWork::BuildClip(clip, img, passnative);
	}

	bool BuildDestRectIntersect(const tpl::Rect& r1, const tpl::Rect& r2)
	{
		return _dst.rect.Intersect(r1, r2);
	}

	void MakeSizeAsDest()
	{
		_size = _dst.rect.Size();
	}

	void MakeStrechSize(const tpl::Rect* rt)
	{
		if (!rt)
			rt = &_dst.rect;
		_stsize.Set(
			(float)rt->Width() / (float)_src.rect.Width(),
			(float)rt->Height() / (float)_src.rect.Height());
		_stretch = !k_eq(_stsize.x, 1.0f) || !k_eq(_stsize.y, 1.0f);
	}

	void SetUpImage(qgImage* src, qgImage* dst)
	{
		_dst.pitch = dst->GetPitch();
		_dst.pmul = dst->GetPmul();
		_dst.data = dst->GetData() + (_dst.rect.top * _dst.pitch) + (_dst.rect.left * _dst.pmul);

		if (!src)
			_src.pitch = _size.x * _dst.pmul;
		else
		{
			_src.pitch = src->GetPitch();
			_src.pmul = src->GetPixelProp().bpp;
			_src.data = src->GetData() + (_src.rect.top * _src.pitch) + (_src.rect.left * _src.pmul);
		}
	}

private:
	void ExecCopyXToX()
	{
		kuint w = _size.x;
		kuint h = _size.y;
		kuint dx, dy;

		if (_stretch)
		{
			kuint* src = (kuint*)_src.data;
			kuint* dst = (kuint*)_dst.data;
			float sw = 1.0f / _stsize.x;
			float sh = 1.0f / _stsize.y;

			for (dy = 0; dy < h; dy++)
			{
				kuint yy = (kuint)(dy * sh);
				src = (kuint*)((kbyte*)_src.data + _src.pitch * yy);

				for (dx = 0; dx < w; dx++)
				{
					kuint xx = (kuint)(dx * sw);
					dst[dx] = src[xx];
				}

				dst = (kuint*)((kbyte*)dst + _dst.pitch);
			}
		}
		else
		{
			kuint pw = _size.x * _dst.pmul;
			kbyte* src = (kbyte*)_src.data;
			kbyte* dst = (kbyte*)_dst.data;

			for (dy = 0; dy < h; dy++)
			{
				memcpy(dst, src, pw);
				src += _src.pitch;
				dst += _dst.pitch;
			}
		}
	}

	//
	void ExecCopy32To16()
	{
		kuint w = _size.x;
		kuint h = _size.y;
		kuint dx, dy;
		kuint* src = (kuint*)_src.data;
		kushort* dst = (kushort*)_dst.data;

		if (_stretch)
		{
			float sw = 1.0f / _stsize.x;
			float sh = 1.0f / _stsize.y;

			for (dy = 0; dy < h; dy++)
			{
				kint yy = (kuint)(dy * sh);
				src = ((kuint*)((kbyte*)_src.data + _src.pitch * yy));

				for (dx = 0; dx < w; dx++)
				{
					kuint xx = (kuint)(dx * sw);
					kuint c = qgBlitWork::PixelLerp(src[xx] | 0xFF000000, qgBlitWork::ExtractAlpha(src[xx]));
					dst[dx] = CCMW::TRN_RGBA32_RGBA16(c);
				}
			}
		}
		else
		{
			for (dy = 0; dy < h; dy++)
			{
				for (dx = 0; dx < w; dx++)
				{
					kuint c = qgBlitWork::PixelLerp(src[dx] | 0xFF000000, qgBlitWork::ExtractAlpha(src[dx]));
					dst[dx] = CCMW::TRN_RGBA32_RGBA16(c);
				}

				src = (kuint*)((kbyte*)src + _src.pitch);
				dst = (kushort*)((kbyte*)dst + _dst.pitch);
			}
		}
	}

	//
	void ExecCopy24To16()
	{
		kuint w = _size.x;
		kuint h = _size.y;
		kuint dx, dy;
		kbyte* src = (kbyte*)_src.data;
		kushort* dst = (kushort*)_dst.data;

		if (_stretch)
		{
			float sw = 3.0f / _stsize.x;
			float sh = 1.0f / _stsize.y;

			for (dy = 0; dy < h; dy++)
			{
				kuint yy = (kuint)(dy * sh);
				src = (kbyte*)_src.data + _src.pitch * yy;

				for (dx = 0; dx < w; dx++)
				{
					kbyte* xx = src + (kuint)(dx * sw);
					dst[dx] = (kushort)(0x8000 | ((xx[0] & 0xF8) << 7) | ((xx[1] & 0xF8) << 2) | ((xx[2] & 0xF8) >> 3));
				}

				dst = (kushort*)((kbyte*)dst + _dst.pitch);
			}
		}
		else
		{
			for (dy = 0; dy < h; dy++)
			{
				kbyte* c = src;

				for (dx = 0; dx < w; dx++)
				{
					dst[dx] = (kushort)(0x8000 | ((c[0] & 0xF8) << 7) | ((c[1] & 0xF8) << 2) | ((c[2] & 0xF8) >> 3));
					c += 3;
				}

				src += _src.pitch;
				dst = (kushort*)((kbyte*)dst + _dst.pitch);
			}
		}
	}

	//
	void ExecCopy16To32()
	{
		kuint w = _size.x;
		kuint h = _size.y;
		kuint dx, dy;
		kushort* src = (kushort*)_src.data;
		kuint* dst = (kuint*)_dst.data;

		if (_stretch)
		{
			float sw = 1.0f / _stsize.x;
			float sh = 1.0f / _stsize.y;

			for (dy = 0; dy < h; dy++)
			{
				kuint yy = (kuint)(dy * sh);
				src = (kushort*)((kbyte*)_src.data + _src.pitch * yy);

				for (dx = 0; dx < w; dx++)
				{
					kuint xx = (kuint)(dx * sw);
					dst[dx] = CCMW::TRN_RGBA16_RGBA32(src[xx]);
				}

				dst = (kuint*)((kbyte*)dst + _dst.pitch);
			}
		}
		else
		{
			for (dy = 0; dy < h; dy++)
			{
				for (dx = 0; dx < w; dx++)
					dst[dx] = CCMW::TRN_RGBA32_RGBA16(src[dx]);

				src = (kushort*)((kbyte*)src + _src.pitch);
				dst = (kuint*)((kbyte*)dst + _dst.pitch);
			}
		}
	}

	//
	void ExecBlend16To16()
	{
		kuint w = _size.x;
		kuint h = _size.y;
		kuint off, dx, dy, rx = w >> 1;
		kuint* src = (kuint*)_src.data;
		kuint* dst = (kuint*)_dst.data;

		if (_stretch)
		{
			float sw = 1.0f / _stsize.x;
			float sh = 1.0f / _stsize.y;
			off = ((-((kint)w & 1) >> 31) & ((kuint)((w - 1) * sw) ^ 0)) ^ 0;

			for (dy = 0; dy < h; dy++)
			{
				kuint yy = (kuint)(dy * sh);
				src = (kuint*)((kbyte*)_src.data + _src.pitch * yy);

				for (dx = 0; dx < rx; dx++)
				{
					kuint xx = (kuint)(dx * sw);
					kuint m32 = ((src[xx] & 0x80008000) >> 15) | 0x7FFF7FFF;
					dst[dx] = (dst[dx] & m32) | (src[xx] & ~m32);
				}

				if (off)
				{
					kuint m16 = ((((kushort*)src)[off] & 0x8000) >> 15) | 0x7FFF;
					((kushort*)dst)[off] = ((((kushort*)dst)[off])&m16) | ((((kushort*)src)[off])&~m16);
				}

				dst = (kuint*)((kbyte*)dst + _dst.pitch);
			}
		}
		else
		{
			off = ((-((kint)w & 1) >> 31) & ((w - 1) ^ 0)) ^ 0;

			for (dy = 0; dy < h; dy++)
			{
				for (dx = 0; dx < rx; dx++)
				{
					kuint m32 = ((src[dx] & 0x80008000) >> 15) | 0x7FFF7FFF;
					dst[dx] = (dst[dx] & m32) | (src[dx] & ~m32);
				}

				if (off)
				{
					kuint m16 = ((((kushort*)src)[off] & 0x8000) >> 15) | 0x7FFF;
					((kushort*)dst)[off] = ((((kushort*)dst)[off])&m16) | ((((kushort*)src)[off])&~m16);
				}

				src = (kuint*)((kbyte*)src + _src.pitch);
				dst = (kuint*)((kbyte*)dst + _dst.pitch);
			}
		}
	}

	//
	void ExecBlend32To32()
	{
		kuint w = _size.x;
		kuint h = _size.y;
		kuint dx, dy, rx = w >> 1;
		kuint* src = (kuint*)_src.data;
		kuint* dst = (kuint*)_dst.data;

		if (_stretch)
		{
			float sw = 1.0f / _stsize.x;
			float sh = 1.0f / _stsize.y;

			for (dy = 0; dy < h; dy++)
			{
				kuint yy = (kuint)(dy * sh);
				src = (kuint*)((kbyte*)_src.data + _src.pitch * yy);

				for (dx = 0; dx < rx; dx++)
				{
					kuint xx = (kuint)(dx * sw);
					dst[dx] = qgBlitWork::PixelInvBlend(dst[dx], src[xx]);
				}

				dst = (kuint*)((kbyte*)dst + _dst.pitch);
			}
		}
		else
		{
			for (dy = 0; dy < h; dy++)
			{
				for (dx = 0; dx < rx; dx++)
					dst[dx] = qgBlitWork::PixelInvBlend(dst[dx], src[dx]);

				src = (kuint*)((kbyte*)src + _src.pitch);
				dst = (kuint*)((kbyte*)dst + _dst.pitch);
			}
		}
	}

	//
	void ExecBlendColor16To16()
	{
		kushort* src = (kushort*)_src.data;
		kushort* dst = (kushort*)_dst.data;
		kushort blend = CCMW::TRN_RGBA32_RGBA16(_argb);
		kushort c0, c1;
		kuint cr;
		kint dx, dy;

		for (dy = 0; dy < _size.y; dy++)
		{
			for (dx = 0; dx < _size.x; dx++)
			{
				if ((src[dx] & 0x8000) == 0)
					continue;

				c0 = src[dx];
				c1 = blend;

				// res=c0*(c1/31);
				cr = ((((c0 & 0x7C00) * (c1 & 0x7C00)) & 0x3E000000) >> 15 |
					(((c0 & 0x03E0) * (c1 & 0x03E0)) & 0x000F8000) >> 10 |
					(((c0 & 0x001F) * (c1 & 0x001F)) & 0x000003E0) >> 5 |
					(c0 & c1 & 0x8000));

				dst[dx] = (kushort)cr;
			}

			src = (kushort*)((kbyte*)src + _src.pitch);
			dst = (kushort*)((kbyte*)dst + _dst.pitch);
		}
	}

	//
	void ExecBlendColor32To32()
	{
		kuint* src = (kuint*)_src.data;
		kuint* dst = (kuint*)_dst.data;
		kuint c0, c1, cr;
		kint dx, dy;

		for (dy = 0; dy < _size.y; dy++)
		{
			for (dx = 0; dx < _size.x; dx++)
			{
				c0 = src[dx];
				c1 = _argb;

				cr = ((((c0 & 0xFF000000) >> 16) * ((c1 & 0xFF000000) >> 16)) & 0xFF000000) |
					((((c0 & 0x00FF0000) >> 12) * ((c1 & 0x00FF0000) >> 12)) & 0x00FF0000) |
					((((c0 & 0x0000FF00) * (c1 & 0x0000FF00)) >> 16) & 0x0000FF00) |
					((((c0 & 0x000000FF) * (c1 & 0x000000FF)) >> 8) & 0x000000FF);

				dst[dx] = qgBlitWork::PixelInvBlend(dst[dx], cr);
			}

			src = (kuint*)((kbyte*)src + _src.pitch);
			dst = (kuint*)((kbyte*)dst + _dst.pitch);
		}
	}

	//
	void ExecColor16To16()
	{
		kushort c = CCMW::TRN_RGBA32_RGBA16(_argb);
		kushort* dst = (kushort*)_dst.data;
		kint dy;

		for (dy = 0; dy < _size.y; dy++)
		{
			kushort* d = dst;
			kint i;

			i = _src.pitch >> (1 + 3);

			while (i)
			{
				d[0] = c;
				d[1] = c;
				d[2] = c;
				d[3] = c;
				d[4] = c;
				d[5] = c;
				d[6] = c;
				d[7] = c;

				d += 8;
				i--;
			}

			i = (_src.pitch >> 1) & 7;

			while (i)
			{
				*d = c;
				d++;
				i--;
			}

			dst = (kushort*)((kbyte*)dst + _dst.pitch);
		}
	}

	//
	void ExecColor32To32()
	{
		kuint c = _argb;
		kuint* dst = (kuint*)_dst.data;
		kint dy;

		for (dy = 0; dy < _size.y; dy++)
		{
			kuint* d = dst;
			kint i;

			i = _src.pitch >> (2 + 3);

			while (i)
			{
				d[0] = c;
				d[1] = c;
				d[2] = c;
				d[3] = c;
				d[4] = c;
				d[5] = c;
				d[6] = c;
				d[7] = c;

				d += 8;
				i--;
			}

			i = (_src.pitch >> 2) & 7;

			while (i)
			{
				*d = c;
				d++;
				i--;
			}

			dst = (kuint*)((kbyte*)dst + _dst.pitch);
		}
	}

	//
	void ExecAlphaColor16To16()
	{
		kushort* dst;
		kushort alpha;
		kuint src;
		kint dx, dy;

		alpha = (kushort)(qgBlitWork::ExtractAlpha(_argb) >> 3);

		if (alpha == 0)
			return;

		dst = (kushort*)_dst.data;
		src = CCMW::TRN_RGBA32_RGBA16(_argb);

		for (dy = 0; dy < _size.y; dy++)
		{
			for (dx = 0; dx < _size.x; dx++)
				dst[dx] = 0x8000 | qgBlitWork::PixelBlend16(dst[dx], src, alpha);

			dst = (kushort*)((kbyte*)dst + _dst.pitch);
		}
	}

	//
	void ExecAlphaColor32To32()
	{
		kuint* dst;
		kuint alpha;
		kuint src;
		kint dx, dy;

		alpha = qgBlitWork::ExtractAlpha(_argb);
		dst = (kuint*)_dst.data;
		src = _argb;

		for (dy = 0; dy < _size.y; dy++)
		{
			for (dx = 0; dx < _size.x; dx++)
				dst[dx] = (_argb & 0xFF000000) | qgBlitWork::PixelBlend(dst[dx], src, alpha);

			dst = (kuint*)((kbyte*)dst + _dst.pitch);
		}
	}

public:
	//
	static void ExecLine32Decl(qgImage* img, const tpl::Point& p0, const tpl::Point& p1, kuint argb)
	{
		kbyte* imgdata = img->GetData();
		kint imgpitch = img->GetPitch();

		// 브렌셀헴 알고리즘
		kint dx = p1.x - p0.x;
		kint dy = p1.x - p0.y;
		kint xinc = 4;
		kint yinc = imgpitch;

		if (dx < 0)
		{
			xinc -= xinc;
			dx = -dx;
		}

		if (dy < 0)
		{
			yinc = -yinc;
			dy = -dy;
		}

		if (dy > dx)
		{
			k_swapi(&dx, &dy);
			k_swapi(&xinc, &yinc);
		}

		//
		kint d = 0;
		kint c = dx << 1;
		kint m = dy << 1;
		kint run = dx;
		kuint* dst = (kuint*)(imgdata + (p0.y * imgpitch) + (p0.x << 2));

		do
		{
			*dst = argb;

			dst = (kuint*)((kbyte*)dst + xinc);
			d += m;

			if (d > dx)
			{
				dst = (kuint*)((kbyte*)dst + yinc);
				d -= c;
			}

			run -= 1;
		} while (run >= 0);
	}

	//
	static void ExecLine32Blend(qgImage* img, const tpl::Point& p0, const tpl::Point& p1, kuint argb, kuint alpha)
	{
		kbyte* imgdata = img->GetData();
		kint imgpitch = img->GetPitch();

		// 브렌셀헴 알고리즘
		kint dx = p1.x - p0.x;
		kint dy = p1.x - p0.y;
		kint xinc = 4;
		kint yinc = imgpitch;

		if (dx < 0)
		{
			xinc -= xinc;
			dx = -dx;
		}

		if (dy < 0)
		{
			yinc = -yinc;
			dy = -dy;
		}

		if (dy > dx)
		{
			k_swapi(&dx, &dy);
			k_swapi(&xinc, &yinc);
		}

		//
		kint d = 0;
		kint c = dx << 1;
		kint m = dy << 1;
		kint run = dx;
		kint pack = qgBlitWork::PackAlpha(alpha);
		kuint* dst = (kuint*)(imgdata + (p0.y * imgpitch) + (p0.x << 2));

		do
		{
			*dst = pack | qgBlitWork::PixelBlend(*dst, argb, alpha);

			dst = (kuint*)((kbyte*)dst + xinc);
			d += m;

			if (d > dx)
			{
				dst = (kuint*)((kbyte*)dst + yinc);
				d -= c;
			}

			run -= 1;
		} while (run >= 0);
	}

	//
	static void ExecLine16Decl(qgImage* img, const tpl::Point& p0, const tpl::Point& p1, kushort argb)
	{
		kbyte* imgdata = img->GetData();
		kint imgpitch = img->GetPitch();

		// 브렌셀헴 알고리즘
		kint dx = p1.x - p0.x;
		kint dy = p1.x - p0.y;
		kint xinc = 2;
		kint yinc = imgpitch;

		if (dx < 0)
		{
			xinc -= xinc;
			dx = -dx;
		}

		if (dy < 0)
		{
			yinc = -yinc;
			dy = -dy;
		}

		if (dy > dx)
		{
			k_swapi(&dx, &dy);
			k_swapi(&xinc, &yinc);
		}

		//
		kint d = 0;
		kint c = dx << 1;
		kint m = dy << 1;
		kint run = dx;
		kushort* dst = (kushort*)(imgdata + (p0.y * imgpitch) + (p0.x << 1));

		do
		{
			*dst = argb;

			dst = (kushort*)((kbyte*)dst + xinc);
			d += m;

			if (d > dx)
			{
				dst = (kushort*)((kbyte*)dst + yinc);
				d -= c;
			}

			run -= 1;
		} while (run >= 0);
	}

	//
	static void ExecLine16Blend(qgImage* img, const tpl::Point& p0, const tpl::Point& p1, kushort argb, kushort alpha)
	{
		kbyte* imgdata = img->GetData();
		kint imgpitch = img->GetPitch();

		// 브렌셀헴 알고리즘
		kint dx = p1.x - p0.x;
		kint dy = p1.x - p0.y;
		kint xinc = 2;
		kint yinc = imgpitch;

		if (dx < 0)
		{
			xinc -= xinc;
			dx = -dx;
		}

		if (dy < 0)
		{
			yinc = -yinc;
			dy = -dy;
		}

		if (dy > dx)
		{
			k_swapi(&dx, &dy);
			k_swapi(&xinc, &yinc);
		}

		//
		kint d = 0;
		kint c = dx << 1;
		kint m = dy << 1;
		kint run = dx;
		kushort pack = alpha ? 0x8000 : 0;
		kushort* dst = (kushort*)(imgdata + (p0.y * imgpitch) + (p0.x << 1));

		do
		{
			*dst = pack | qgBlitWork::PixelBlend16(*dst, argb, alpha);

			dst = (kushort*)((kbyte*)dst + xinc);
			d += m;

			if (d > dx)
			{
				dst = (kushort*)((kbyte*)dst + yinc);
				d -= c;
			}

			run -= 1;
		} while (run >= 0);
	}

public:
	static BlitFunc GetFunc(qgBlitOp op, qgImage* dst, qgImage* src)
	{
		static struct BlitTable
		{
			qgBlitOp		op;
			kint			dst;
			kint			src;
			BlitFunc		func;
		} s_blits[] =
		{
			{QG_BLITOP_TEX, -2, -2, &BlitData::ExecCopyXToX},
			{QG_BLITOP_TEX, QGCF16_RGBA, QGCF32_RGBA, &BlitData::ExecCopy32To16},
			{QG_BLITOP_TEX, QGCF32_RGBA, QGCF16_RGBA, &BlitData::ExecCopy16To32},
			{QG_BLITOP_TEX_ALPHA, QGCF16_RGBA, QGCF16_RGBA, &BlitData::ExecBlend16To16},
			{QG_BLITOP_TEX_ALPHA, QGCF32_RGBA, QGCF32_RGBA, &BlitData::ExecBlend32To32},
			{QG_BLITOP_TEX_ALPHA_COLOR, QGCF16_RGBA, QGCF16_RGBA, &BlitData::ExecBlendColor16To16},
			{QG_BLITOP_TEX_ALPHA_COLOR, QGCF32_RGBA, QGCF32_RGBA, &BlitData::ExecBlendColor32To32},
			{QG_BLITOP_COLOR, QGCF16_RGBA, -1, &BlitData::ExecColor16To16},
			{QG_BLITOP_COLOR, QGCF32_RGBA, -1, &BlitData::ExecColor32To32},
			{QG_BLITOP_ALPHA_COLOR, QGCF16_RGBA, -1, &BlitData::ExecAlphaColor16To16},
			{QG_BLITOP_ALPHA_COLOR, QGCF32_RGBA, -1, &BlitData::ExecAlphaColor32To32},
			{QG_BLITOP_NONE, QGCF_NONE, QGCF_NONE, NULL}
		};

		kint sfmt = src ? src->GetPixelProp().fmt : QGCF_NONE;
		kint dfmt = dst ? dst->GetPixelProp().fmt : QGCF_NONE;
		struct BlitTable* t = s_blits;

		while (t->op != QG_BLITOP_NONE)
		{
			if (t->op == op)
			{
				if ((t->dst == dfmt || t->dst == -1) &&
					(t->src == sfmt || t->src == -1))
					return t->func;
				else if (t->dst == -2 && sfmt == dfmt)
					return t->func;
			}

			t++;
		}

		return NULL;
	}

};


//////////////////////////////////////////////////////////////////////////
// 블릿 이미지

//
bool qgBlitImage::Blit(qgBlitOp op, qgImage* dst, const tpl::Rect* drect, const tpl::Point& dpos, qgImage* src, const tpl::Rect* srect, const tpl::ColorValue& argb)
{
	BlitData::BlitFunc func = BlitData::GetFunc(op, dst, src);
	k_return_value_if_fail(func, false);

	tpl::Rect csrc = qgBlitWork::BuildClip(srect, src, true);
	tpl::Rect cdst = qgBlitWork::BuildClip(drect, dst, false);
	tpl::Rect v(dpos, csrc.Size());

	BlitData bd(argb);

	if (!bd.BuildDestRectIntersect(cdst, v))
		return false;

	bd.MakeSizeAsDest();
	bd.BuildSourceRect(csrc.Location(), v.Location());
	bd.SetUpImage(src, dst);

	((bd).*func)();

	return TRUE;
}

//
bool qgBlitImage::StrechBlit(qgBlitOp op, qgImage* dst, const tpl::Rect* drect, qgImage* src, const tpl::Rect* srect, const tpl::ColorValue& argb)
{
	BlitData::BlitFunc func = BlitData::GetFunc(op, dst, src);
	k_return_value_if_fail(func, false);

	BlitData bd(argb);

	bd.BuildDestRect(drect, dst);
	bd.BuildSourceRect(srect, src);
	bd.MakeSizeAsDest();
	bd.MakeStrechSize(drect);
	bd.SetUpImage(src, dst);

	((bd).*func)();

	return TRUE;
}

//
void qgBlitImage::DrawRect(qgImage* img, const tpl::Rect& rt, const tpl::ColorValue& cc)
{
	Blit(cc.a == 0xFF ? QG_BLITOP_COLOR : QG_BLITOP_ALPHA_COLOR, img, NULL, rt.Location(), NULL, &rt, cc);
}

//
void qgBlitImage::DrawLine(qgImage* img, const tpl::Point& start, const tpl::Point& end, const tpl::ColorValue& cc)
{
	tpl::Rect clip = qgBlitWork::MakeClip(img);
	tpl::Point p0, p1;

	if (qgBlitWork::ClipLine(clip, &p0, &p1, start, end))
	{
		kuint alpha = qgBlitWork::ExtractAlpha(cc.ToUint());
		qgColorFormat fmt = img->GetColorFormat();

		if (fmt == QGCF16_RGBA)
		{
			if (alpha == 0xFF)
				BlitData::ExecLine16Decl(img, p0, p1, CCMW::TRN_RGBA32_RGBA16(cc.ToUint()));
			else
				BlitData::ExecLine16Blend(img, p0, p1, CCMW::TRN_RGBA32_RGBA16(cc.ToUint()), (kushort)(alpha >> 3));
		}
		else if (fmt == QGCF32_RGBA)
		{
			if (alpha == 0xFF)
				BlitData::ExecLine32Decl(img, p0, p1, cc.ToUint());
			else
				BlitData::ExecLine32Blend(img, p0, p1, cc.ToUint(), alpha);
		}
	}
}

